page.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. 'use client';
  2. import './style.scss';
  3. import '../../crew/widget/style.scss';
  4. import { useState, useEffect, useCallback } from 'react';
  5. import { useParams } from 'next/navigation';
  6. import Link from 'next/link';
  7. import { fetchApi } from '@/lib/utils/client';
  8. import { useStudioContext } from '@/app/studio/context';
  9. import type { CrewItem, CrewListResponse } from '@/types/response/crew/list';
  10. import { Button } from '@/components/ui/button';
  11. import CrewSettingsTab from './_components/CrewSettingsTab';
  12. import CrewMembersTab from './_components/CrewMembersTab';
  13. import CrewWidgetTab from './_components/CrewWidgetTab';
  14. import CrewSessionTab from './_components/CrewSessionTab';
  15. const TABS = [
  16. { key: 'settings', label: '기본 설정' },
  17. { key: 'members', label: '크루원' },
  18. { key: 'widget', label: '위젯' },
  19. { key: 'session', label: '세션' }
  20. ] as const;
  21. type TabKey = typeof TABS[number]['key'];
  22. export default function CrewDetailPage()
  23. {
  24. const { id } = useParams<{ id: string }>();
  25. const { channelID } = useStudioContext();
  26. const crewID = Number(id);
  27. const [crew, setCrew] = useState<CrewItem|null>(null);
  28. const [loading, setLoading] = useState(true);
  29. const [activeTab, setActiveTab] = useState<TabKey>('members');
  30. const fetchCrew = useCallback(async () => {
  31. if (!channelID) return;
  32. try {
  33. const res = await fetchApi<CrewListResponse>(`/api/studio/crew/list/${channelID}`);
  34. const found = res.data?.list.find(c => c.id === crewID);
  35. if (found) setCrew(found);
  36. } catch {}
  37. }, [channelID, crewID]);
  38. useEffect(() => {
  39. setLoading(true);
  40. fetchCrew().finally(() => setLoading(false));
  41. }, [fetchCrew]);
  42. if (!channelID) {
  43. return (
  44. <div className="studio-page">
  45. <p className="studio-page__empty">채널을 먼저 연동해 주세요.</p>
  46. </div>
  47. );
  48. }
  49. if (loading) {
  50. return <div className="studio-page"><p className="studio-page__empty">준비 중...</p></div>;
  51. }
  52. if (!crew) {
  53. return <div className="studio-page"><p className="studio-page__empty">크루를 찾을 수 없습니다.</p></div>;
  54. }
  55. return (
  56. <div className="studio-page crew-detail">
  57. {/* 헤더: 제목 + 액션 버튼 */}
  58. <div className="crew-detail__header">
  59. <div className="crew-detail__info">
  60. <h1 className="crew-detail__title">{crew.name}</h1>
  61. {crew.description && <p className="crew-detail__desc">{crew.description}</p>}
  62. </div>
  63. <div className="crew-detail__actions">
  64. <Button variant="outline" size="sm" asChild>
  65. <Link href="/studio/donation/crew">< 목록으로</Link>
  66. </Button>
  67. </div>
  68. </div>
  69. {/* 탭 네비게이션 */}
  70. <nav className="crew-tabs">
  71. {TABS.map(tab => (
  72. <button
  73. type="button"
  74. key={tab.key}
  75. className={`crew-tabs__item${activeTab === tab.key ? ' crew-tabs__item--active' : ''}`}
  76. onClick={() => setActiveTab(tab.key)}
  77. >
  78. {tab.label}
  79. </button>
  80. ))}
  81. </nav>
  82. {/* 탭 컨텐츠 */}
  83. <div className="crew-tabs__content">
  84. {activeTab === 'settings' && <CrewSettingsTab crew={crew} onUpdated={fetchCrew} />}
  85. {activeTab === 'members' && <CrewMembersTab crewID={crewID} />}
  86. {activeTab === 'widget' && <CrewWidgetTab crewID={crewID} />}
  87. {activeTab === 'session' && <CrewSessionTab crewID={crewID} />}
  88. </div>
  89. </div>
  90. );
  91. }